home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / ddj0290.arc / STROYAN.LST < prev   
File List  |  1990-01-07  |  18KB  |  599 lines

  1. _THREE-DIMENSIONAL GRAPHICS USING THE X-WINDOW SYSTEM_
  2. by Michael Stroyan
  3.  
  4. [LISTING ONE]
  5.  
  6. /* example.c - An example of three dimensional graphics using Xlib. */
  7.  
  8. #include <X11/Xlib.h>
  9. #include <X11/Xutil.h>
  10. #include <math.h>
  11. #include <stdio.h>
  12. #include "double_buffer.h"
  13.  
  14. typedef double Transform[4][4];
  15. typedef double Point[3];
  16.  
  17. Display *display;           /* display connection */
  18. Window window;              /* window identifier */
  19. GC gc;                      /* graphics context */
  20. XColor colors[4];           /* colors to draw with */
  21. double_buffer_state *dbuf_state; /* state record for double buffer utilities */
  22. double_buffer = 1;          /* Whether to use double buffering */
  23. unsigned int width, height; /* last known window size */
  24. Transform model;            /* transform from world to modelling coordinates */
  25. Transform view;             /* transform from modelling to VDC coordinates */
  26. Transform device;           /* transform from VDC to device coordinates */
  27. Transform composite;        /* transform from world to device coordinates */
  28. Transform motion;           /* transform for modelling motion */
  29.  
  30. identity(transform)
  31. /* Set a Transform matrix to an identity matrix. */
  32. Transform transform; /* transform to operate on */
  33. {
  34.     register int i, j;
  35.  
  36.     for (i = 0; i < 4; i++)
  37.         for (j = 0; j < 4; j++)
  38.             transform[i][j] = (i == j);
  39. }
  40.  
  41. rotate_X(transform, angle)
  42. /* Set a Transform matrix to a rotation around the X axis. */
  43. Transform transform; /* transform to operate on */
  44. double angle;        /* angle in radians to rotate by */
  45. {
  46.     identity(transform);
  47.     transform[1][1] = transform[2][2] = cos(angle);
  48.     transform[2][1] = -(transform[1][2] = sin(angle));
  49. }
  50.  
  51. rotate_Y(transform, angle)
  52. /* Set a Transform matrix to a rotation around the Y axis. */
  53. Transform transform; /* transform to operate on */
  54. double angle;        /* angle in radians to rotate by */
  55. {
  56.     identity(transform);
  57.     transform[0][0] = transform[2][2] = cos(angle);
  58.     transform[0][2] = -(transform[2][0] = sin(angle));
  59. }
  60.  
  61. transform_point(transform, p, tp)
  62. /* Apply a Transform matrix to a point. */
  63. Transform transform; /* transform to apply to the point */
  64. Point p;             /* the point to transform */
  65. Point tp;            /* the returned point after transformation */
  66. {
  67.     int i, j;
  68.     double homogeneous[4];
  69.     double sum;
  70.  
  71.     for (i = 0; i < 4; i++) {
  72.         sum = 0.0;
  73.         for (j = 0; j < 3; j++)
  74.             sum += p[j] * transform[j][i];
  75.         homogeneous[i] = sum + transform[3][i];
  76.     }
  77.  
  78.     for (i = 0; i < 3; i++)
  79.         tp[i] = homogeneous[i] / homogeneous[3];
  80. }
  81.  
  82. cross_product(v1, v2, c)
  83. /* Compute the cross product of two vectors. */
  84. Point v1, v2; /* the vectors to take the cross product of */
  85. Point c;      /* the result */
  86. {
  87.     c[0] = v1[1] * v2[2] - v1[2] * v2[1];
  88.     c[1] = v1[2] * v2[0] - v1[0] * v2[2];
  89.     c[2] = v1[0] * v2[1] - v1[1] * v2[0];
  90. }
  91.  
  92. backface(p1, p2, p3)
  93. /* Determine if a polygon is a back face of an object */
  94. Point p1, p2, p3; /* the first three vertices of a polygon */
  95. {
  96.     Point v1, v2, c;
  97.  
  98.     /* This relies on the first three vertices of each face being clockwise
  99.      * around a convex angle or counter-clockwise around a concave
  100.      * angle as viewed from the front of the face. */
  101.     v1[0] = p2[0] - p1[0];
  102.     v1[1] = p2[1] - p1[1];
  103.     v1[2] = p2[2] - p1[2];
  104.     v2[0] = p2[0] - p3[0];
  105.     v2[1] = p2[1] - p3[1];
  106.     v2[2] = p2[2] - p3[2];
  107.     cross_product(v1, v2, c);
  108.     return(c[2] > 0);
  109. }
  110.  
  111. concatenate_transforms(transform1, transform2, result)
  112. /* Use matrix multiplication to combine two transforms into one. */
  113. Transform transform1, transform2; /* the transforms to combine */
  114. Transform result;                 /* the new combined transform */
  115. {
  116.     register int i, j, k; /* index variables */
  117.     Transform temporary;  /* a temporary result */
  118.     /* Using a temporary result allows a single transform to be passed in
  119.      * as both one of the original transforms and as the new result. */
  120.  
  121.     for (i = 0; i < 4; i++) {
  122.         for (j = 0; j < 4; j++) {
  123.             temporary[i][j] = 0.0;
  124.             for (k = 0; k < 4; k++) {
  125.                 temporary[i][j] += transform1[i][k] * transform2[k][j];
  126.             }
  127.         }
  128.     }
  129.  
  130.     for (i = 0; i < 4; i++)
  131.         for (j = 0; j < 4; j++)
  132.             result[i][j] = temporary[i][j];
  133. }
  134.  
  135. init_X(argc, argv)
  136. /* Initialize the X window system */
  137. int argc;  /* the number of program arguments */
  138. char *argv[]; /* an array of pointers to program arguments */
  139. {
  140.     XEvent event;                /* holds X server events */
  141.     static XSizeHints xsh = {           /* Size hints for window manager */
  142.         (PPosition | PSize | PMinSize), /* flags */
  143.         300,                            /* height */
  144.         300,                            /* width */
  145.         200,                            /* minimum height */
  146.         200,                            /* minimum width */
  147.         5,                              /* x coordinate */
  148.         5                               /* y coordinate */
  149.     };
  150.     static XWMHints xwmh = {      /* More hints for window manager */
  151.         (InputHint | StateHint),  /* flags */
  152.         False,                    /* input */
  153.         NormalState,              /* initial_state */
  154.         0,                        /* icon pixmap */
  155.         0,                        /* icon window */
  156.         0, 0,                     /* icon location */
  157.         0,                        /* icon mask */
  158.         0,                        /* Window group */
  159.     };
  160.     static XClassHint xch = {     /* Class hints for window manager */
  161.         "example",                /* name */
  162.         "EXample"                 /* class */
  163.     };
  164.     XGCValues gcvalues;
  165.  
  166.     if ((display = XOpenDisplay(NULL)) == NULL) {
  167.         fprintf(stderr, "Can't open %s\n", XDisplayName(NULL));
  168.         exit(1);
  169.     }
  170.  
  171.     window = XCreateSimpleWindow(display,
  172.         DefaultRootWindow(display),
  173.         xsh.x, xsh.y,   xsh.width, xsh.height,   2,
  174.         WhitePixel(display, DefaultScreen(display)),
  175.         BlackPixel(display, DefaultScreen(display)));
  176.  
  177.     XSetStandardProperties(display, window, "Example", "Example",
  178.         None, argv, argc, &xsh);
  179.     XSetWMHints(display, window, &xwmh);
  180.     XSetClassHint(display, window, &xch);
  181.  
  182.     XSelectInput(display, window,
  183.         StructureNotifyMask | 
  184.         ExposureMask | 
  185.         ButtonPressMask | 
  186.         Button1MotionMask | 
  187.         PointerMotionHintMask);
  188.  
  189.     XMapWindow(display, window);
  190.     XFlush(display);
  191.  
  192.     do {
  193.         XNextEvent(display, &event);
  194.     } while (event.type != MapNotify || event.xmap.window != window);
  195.  
  196.     gc = XCreateGC(display, window, 0, &gcvalues);
  197.     XSetState(display, gc, 
  198.         WhitePixel(display, DefaultScreen(display)),
  199.         BlackPixel(display, DefaultScreen(display)),
  200.         GXcopy, AllPlanes);
  201.  
  202.     /* black */
  203.     colors[0].red = 0;
  204.     colors[0].green = 0;
  205.     colors[0].blue = 0;
  206.  
  207.     /* white */
  208.     colors[1].red = 65535;
  209.     colors[1].green = 65535;
  210.     colors[1].blue = 65535;
  211.  
  212.     /* green */
  213.     colors[2].red = 0;
  214.     colors[2].green = 40000;
  215.     colors[2].blue = 0;
  216.  
  217.     /* yellow */
  218.     colors[3].red = 65535;
  219.     colors[3].green = 65535;
  220.     colors[3].blue = 0;
  221.  
  222.     dbuf_state = start_double_buffer(display,
  223.         DefaultColormap(display, DefaultScreen(display)), 2, colors);
  224.     if (dbuf_state == NULL) {
  225.          fprintf(stderr, "Couldn't allocate resources for double buffering\n");
  226.      exit(1);
  227.     }
  228.     XSetPlaneMask(display, gc, dbuf_state->drawing_planes);
  229. }
  230.  
  231. init_transforms()
  232. /* Initialize transformations for modelling, viewing, and device mapping. */
  233. {
  234.     Window root;
  235.     int x, y;
  236.     unsigned int b, d;
  237.  
  238.     identity(model);
  239.  
  240.     identity(view);
  241.     view[2][2] = 2.0;
  242.     view[2][3] = 1.0;
  243.     view[3][2] = 29.0;
  244.     view[3][3] = 15.0;
  245.  
  246.     XGetGeometry(display, window, &root, &x, &y, &width, &height, &b, &d);
  247.     identity(device);
  248.     device[0][0] = device[3][0] = width / 2.0;
  249.     device[1][1] = device[3][1] = height / 2.0;
  250.  
  251.     concatenate_transforms(model, view, composite);
  252.     concatenate_transforms(composite, device, composite);
  253. }
  254.  
  255. #define POINTS 16     /* The total number of unique points */
  256. #define POLYPOINTS 12 /* The maximum number of vertices in a polygon */
  257. #define PARTIALS 6    /* The maximum number of partial polygons in a polygon */
  258.  
  259. #define END_POLYGON -2         /* designates the end of one polygon */
  260. #define END_POLYGONS -3        /* designates the end of all polygons */
  261. #define END_PARTIAL_POLYGON -1 /* designates the end of a partial polygon */
  262.  
  263. redraw()
  264. /* Draw the 3d object using the current composite transform. */
  265. {
  266.     static Point points[POINTS] = {
  267.         -4.0, -4.0, -4.0,
  268.          4.0, -4.0, -4.0,
  269.         -4.0,  4.0, -4.0,
  270.          4.0,  4.0, -4.0,
  271.         -4.0, -4.0,  4.0,
  272.          4.0, -4.0,  4.0,
  273.         -4.0,  4.0,  4.0,
  274.          4.0,  4.0,  4.0,
  275.         -2.0, -4.0, -2.0,
  276.          2.0, -4.0, -2.0,
  277.         -2.0,  0.0, -2.0,
  278.          2.0,  0.0, -2.0,
  279.         -2.0, -4.0,  2.0,
  280.          2.0, -4.0,  2.0,
  281.         -2.0,  0.0,  2.0,
  282.          2.0,  0.0,  2.0,
  283.     };
  284.     Point transformed_points[POINTS];
  285.     static int polygons[] = {
  286.  
  287.         10, 11,  9,  8, 10, END_POLYGON,
  288.         12, 13, 15, 14, 12, END_POLYGON,
  289.         9, 11, 15, 13,  9, END_POLYGON,
  290.         11, 10, 14, 15, 11, END_POLYGON,
  291.         10,  8, 12, 14, 10, END_POLYGON,
  292.  
  293.         0,  4,  5,  1,  0, END_PARTIAL_POLYGON,
  294.         8, 12, 13,  9,  8, END_POLYGON,
  295.  
  296.         0,  1,  3,  2,  0, END_POLYGON,
  297.         6,  7,  5,  4,  6, END_POLYGON,
  298.         5,  7,  3,  1,  5, END_POLYGON,
  299.         7,  6,  2,  3,  7, END_POLYGON,
  300.         6,  4,  0,  2,  6, END_POLYGON,
  301.  
  302.         END_POLYGONS,
  303.     };
  304.     XPoint buffer[POLYPOINTS]; /* a set of Xlib coordinate vertices */
  305.     int partials[PARTIALS];    /* starting points of partial polygons */
  306.     int num_partials;          /* number of partial polygons in a polygon*/
  307.     int src;                   /* an index into buffer[] */
  308.     int dest;                  /* an index into polygons[] */
  309.     int i;                     /* an index into partials[] */
  310.     int at_start_of_polygon;   /* flags the start of each polygon */
  311.     int skip_polygon;          /* flags a backface polygon */
  312.  
  313.     XSetForeground(display, gc, colors[0].pixel);
  314.     XFillRectangle(display, window, gc, 0, 0, width, height);
  315.  
  316.     for (i = POINTS - 1; i >= 0; i--)
  317.         transform_point(composite, points[i], transformed_points[i]);
  318.  
  319.     dest = 0;
  320.     at_start_of_polygon = True;
  321.     partials[0] = 0;
  322.     num_partials = 1;
  323.     for (src = 0; polygons[src] != END_POLYGONS; src++) {
  324.         if (at_start_of_polygon) {
  325.             skip_polygon = backface(
  326.                 transformed_points[polygons[src]],
  327.                 transformed_points[polygons[src+1]],
  328.                 transformed_points[polygons[src+2]]);
  329.             at_start_of_polygon = False;
  330.         }
  331.         switch (polygons[src]) {
  332.         case END_POLYGON:
  333.           if (!skip_polygon) {
  334.              XSetForeground(display, gc, colors[2].pixel);
  335.              XFillPolygon(display, window, gc, &buffer[0], dest,
  336.              Complex, CoordModeOrigin);
  337.              XSetForeground(display, gc, colors[1].pixel);
  338.              partials[num_partials] = dest;
  339.              for (i=0; i<num_partials; i++)
  340.               XDrawLines(display, window, gc, &buffer[partials[i]],
  341.                   partials[i+1] - partials[i], CoordModeOrigin);
  342.         }
  343.         dest = 0;
  344.         at_start_of_polygon = True;
  345.         break;
  346.        case END_PARTIAL_POLYGON:
  347.         partials[num_partials++] = dest;
  348.         break;
  349.        default:
  350.         buffer[dest].x = transformed_points[polygons[src]][0];
  351.         buffer[dest++].y = transformed_points[polygons[src]][1];
  352.         break;
  353.        }
  354.     }
  355.     if (double_buffer) {
  356.         double_buffer_switch(dbuf_state);
  357.         XSetPlaneMask(display, gc, dbuf_state->drawing_planes);
  358.     } else {
  359.         XSetPlaneMask(display, gc, AllPlanes);
  360.     }
  361.     XFlush(display);
  362. }
  363.  
  364. main(argc, argv)
  365. char *argv[];
  366. {
  367.     XEvent event;             /* holds X server events */
  368.     int x, y;                 /* the last X pointer position */
  369.     int new_x, new_y;         /* a new X pointer position */
  370.     unsigned int mask;        /* mask of button and modifier key state */
  371.     unsigned int dummy;       /* placeholder for unwanted return values */
  372.  
  373.     init_X(argc, argv);
  374.     init_transforms();
  375.  
  376.     printf("Drag button 1 to rotate the object.\n");
  377.     printf("Press button 2 to toggle double buffering on and off.\n");
  378.     printf("Press button 3 to stop the program.\n");
  379.  
  380.     for (; ; ) {
  381.         XNextEvent(display, &event);
  382.         switch (event.type) {
  383.         case DestroyNotify:
  384.             XCloseDisplay(display);
  385.             exit(0);
  386.             break;
  387.         case Expose:
  388.             if (event.xexpose.count == 0) {
  389.                 redraw();
  390.             }
  391.             break;
  392.         case ConfigureNotify:
  393.            if ((event.xconfigure.width != width) || 
  394.             (event.xconfigure.height != height)) {
  395.             width = event.xconfigure.width;
  396.             height = event.xconfigure.height;
  397.  
  398.             device[0][0] = device[3][0] = width / 2.0;
  399.             device[1][1] = device[3][1] = height / 2.0;
  400.  
  401.             concatenate_transforms(model, view, composite);
  402.             concatenate_transforms(composite, device, composite);
  403.             redraw();
  404.           }
  405.           break;
  406.         case MotionNotify:
  407.             XQueryPointer(display, window,
  408.                 &dummy, &dummy, 
  409.                 &dummy, &dummy, 
  410.                 &new_x, &new_y, 
  411.                 &mask);
  412.             if (!(mask & Button1Mask))
  413.                 break;
  414.             rotate_X(motion, M_PI / 360.0 * (new_y - y));
  415.             concatenate_transforms(model, motion, model);
  416.             rotate_Y(motion, -M_PI / 360.0 * (new_x - x));
  417.             concatenate_transforms(model, motion, model);
  418.             x = new_x;
  419.             y = new_y;
  420.             concatenate_transforms(model, view, composite);
  421.             concatenate_transforms(composite, device, composite);
  422.             redraw();
  423.             break;
  424.         case ButtonPress:
  425.             x = event.xbutton.x;
  426.             y = event.xbutton.y;
  427.             if (event.xbutton.button == 2)
  428.                 double_buffer ^= 1;
  429.             if (event.xbutton.button == 3)
  430.                 exit(0);
  431.             break;
  432.         default:
  433.             break;
  434.         }
  435.     }
  436. }
  437.  
  438.  
  439.  
  440. [LISTING TWO]
  441.  
  442. /* double_buffer.h - declarations for an Xlib double buffering utility. */
  443.  
  444. /* double buffering state record */
  445. typedef struct {
  446.     Display *display;
  447.     Colormap cmap;
  448.     long drawing_planes;     /* planes currently drawn to */
  449.     int buffer;              /* which buffer to show, even or odd */
  450.     XColor *colormaps[2];    /* color maps for even and odd buffers */
  451.     int map_size;            /* number of entries in color maps */
  452.     long masks[2];           /* write_enable masks for odd and even */
  453.     long *planes;            /* individual planes */
  454.     long pixel;              /* pixel base value of double buffering */
  455. } double_buffer_state;
  456.  
  457. /* double buffering procedures */
  458. extern double_buffer_state *start_double_buffer();
  459. extern void double_buffer_switch();
  460. extern void end_double_buffer();
  461.  
  462.  
  463. [LISTING THREE]
  464.  
  465. /* double_buffer.c - an Xlib double buffering utility. */
  466.  
  467. #include <X11/Xlib.h>
  468. #include <malloc.h>
  469. #include <stdio.h>
  470. #include "double_buffer.h"
  471.  
  472. static void release(state)
  473. register double_buffer_state *state;
  474. /* Release a possibly partially allocated double buffer state record. */
  475. {
  476.     if (state != NULL) {
  477.         if (state->colormaps[0] != NULL) free(state->colormaps[0]);
  478.         if (state->colormaps[1] != NULL) free(state->colormaps[1]);
  479.         if (state->planes != NULL) free(state->planes);
  480.         free(state);
  481.     }
  482. }
  483.  
  484. static long color(state, simple_color)
  485. register double_buffer_state *state;
  486. register long simple_color;
  487. /* Map the supplied color into the equivalent color
  488.  * using the double buffered planes. */
  489. {
  490.     register long i, plane, computed_color;
  491.  
  492.     computed_color = state->pixel;
  493.     for (plane = 1, i = 0; simple_color != 0; plane <<= 1, i++) {
  494.         if (plane & simple_color) {
  495.             computed_color |= state->planes[i];
  496.             simple_color &= ~plane;
  497.         }
  498.     }
  499.     return(computed_color);
  500. }
  501.  
  502. double_buffer_state *start_double_buffer(display, cmap, planes, colors)
  503. Display *display;
  504. Colormap cmap;
  505. long planes;    /* how many planes for each buffer */
  506. XColor *colors; /* color settings for buffers */
  507. /* Start double buffering in given number of planes per buffer.
  508.  * If resources can be allocated, then set color pixels in colors parameter
  509.  * and return the address of a double_buffer_state record.
  510.  * Otherwise, return NULL.  */
  511. {
  512.     register double_buffer_state *state;
  513.     register long i, high_mask, low_mask;
  514.  
  515.     /* Allocate memory. */
  516.     state = (double_buffer_state *) malloc(sizeof(double_buffer_state));
  517.     if (state == NULL)
  518.         return (NULL);
  519.  
  520.      state->map_size = 1 << (2 * planes);
  521.      state->colormaps[0] = (XColor *) malloc(state->map_size * sizeof(XColor));
  522.      state->colormaps[1] = (XColor *) malloc(state->map_size * sizeof(XColor));
  523.      state->planes = (long *) malloc((2 * planes) * sizeof(long));
  524.      if (state->colormaps[1] == NULL || state->colormaps[0] == NULL
  525.         || state->planes == NULL) {
  526.         release(state);
  527.         return(NULL);
  528.      }
  529.      state->display = display;
  530.      state->cmap = cmap;
  531.  
  532.     /* Get colors to double buffer with. */
  533.     if (XAllocColorCells(state->display, state->cmap, False,
  534.         state->planes, 2*planes, &state->pixel, 1) == 0) {
  535.         release(state);
  536.         return(NULL);
  537.     }
  538.  
  539.     /* Prepare the write enable masks. */
  540.     state->masks[0] = AllPlanes;
  541.     state->masks[1] = AllPlanes;
  542.     /* Mask 0 won't write in the "low" planes. */
  543.     /* Mask 1 won't write in the "high" planes. */
  544.     for (i = 0; i < planes; i++) {
  545.         state->masks[0] &= ~state->planes[i];
  546.         state->masks[1] &= ~state->planes[planes + i];
  547.     }
  548.  
  549.     /* Prepare the flags and pixel values for each color. */
  550.     for (i = 0; i < (1 << planes); i++) {
  551.         colors[i].pixel = color(state, i | (i << planes));
  552.         colors[i].flags = DoRed | DoGreen | DoBlue;
  553.     }
  554.  
  555.     /* Prepare the two color map settings. */
  556.     /* Colormap 0 displays the "low" planes. */
  557.     /* Colormap 1 displays the "high" planes. */
  558.     low_mask = (1 << planes) - 1;
  559.     high_mask = low_mask << planes;
  560.     for (i = state->map_size - 1; i >= 0; i--) {
  561.         state->colormaps[0][i] = colors[i & low_mask];
  562.         state->colormaps[0][i].pixel = color(state, i);
  563.  
  564.         state->colormaps[1][i] = colors[(i & high_mask) >> planes];
  565.         state->colormaps[1][i].pixel = color(state, i);
  566.     }
  567.  
  568.     /* Set up initial color map and write_enable. */
  569.     state->buffer = 0;
  570.     state->drawing_planes = state->masks[state->buffer];
  571.     XStoreColors(state->display, state->cmap,
  572.         state->colormaps[state->buffer], state->map_size);
  573.  
  574.     return(state);
  575. }
  576.  
  577. void double_buffer_switch(state)
  578. register double_buffer_state *state;
  579. /* Change double buffering buffer.
  580.  * Return the new planes mask for double buffering. */
  581. {
  582.     /* Toggle the buffers. */
  583.     state->buffer ^= 1;
  584.  
  585.     /* Adjust the color map and write enable mask. */
  586.     XStoreColors(state->display, state->cmap,
  587.         state->colormaps[state->buffer], state->map_size);
  588.  
  589.     state->drawing_planes = state->masks[state->buffer];
  590. }
  591.  
  592. void end_double_buffer(state)
  593. register double_buffer_state *state;
  594. {
  595.     XFreeColors(state->display, state->cmap,
  596.         &state->pixel, 1, ~(state->masks[0] & state->masks[1]));
  597.     release(state);
  598. }
  599.